﻿#include "chinese.h"
/*! ************************************************************
 * \file
 * \brief 时间类处理封装
 *
 * \authors 林胜勇
 *
 * \date
 * \verbatim
 * 变更记录：
 * 2023-11-30，林胜勇:
 *   创建文件
 * \endverbatim
 ****************************************************************/
#pragma once

#include <string>
#include <time.h>

#include "timespan.h"

namespace cppc {

/// 日期工具类
class DateTime {
public:
    /// 返回当前系统时区，比如UTC+8的时区为8
    static int localTimeZone();

    /// 判断是否是闰年
    static bool isLeapYear(int year);

    /// 某年某月一共有多少天
    /// \param year 年，如2023
    /// \param month 月，[1-12],超出这个范围返回0
    static int getYearMonthDays(int year, int month);

    /// 获取当前系统运行时间，从开机开始计时，单位毫秒，此时间不受系统日期修改的影响。
    static int64_t currentTickCount();

    /// 获取当前系统时间
    static DateTime currentDateTime();

    /// 获取当前UTC时间缀，毫秒
    static int64_t currentTimeStamp();

public:
    /// 以当前时间构造
    DateTime();

    /// 以UTC时间戳(毫秒)构造
    /// \param stamp UTC时间戳
    /// \param stampTimeZone 时区，默认当前系统时区
    DateTime(int64_t stamp, int stampTimeZone = DateTime::localTimeZone());

    /// 使用tm结构初始化
    /// \param tm 时间，需设置tm_gmtoff来表示时区，单位是秒，东8区是8*3600
    DateTime(const struct tm &time);

    /// 以指定时间构造
    /// Windows下最小为1970-01-01 00:00:00
    /// \param year 年，取值范围[1970, )
    /// \param month 月，取值范围[1,12]
    /// \param day 日，取值范围[1,31]
    /// \param hour 时，取值范围[0,23]，默认为0
    /// \param minute 分，取值范围[0,59]，默认为0
    /// \param second 秒，取值范围[0,60]，默认为0
    /// \param millisecond 毫秒，取值范围[0,999]，默认为0
    /// \param stampTimeZone 时区，默认当前系统时区
    DateTime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0,
             int millisecond = 0, int stampTimeZone = DateTime::localTimeZone());

    /// 使用时间字符串初始化
    /// \param timeStr 时间字符串
    /// \param format 格式，类似ISO 8601的ISODATE格式，如：yyyy-MM-dd HH:mm:ss.zzz
    DateTime(const std::string &timeStr, const std::string &format,
             int stampTimeZone = DateTime::localTimeZone());

    /// 以Date对象复制
    DateTime(const DateTime &other);

    ~DateTime();

    /// 克隆当前对象
    DateTime clone() const;

public:
    /// 获取TM结构数据
    struct tm getTm() const;

    /// 格式化为字符串
    /// \param format 格式，类似ISO 8601的ISODATE格式，如：yyyy-MM-dd HH:mm:ss.zzz
    /// \return 格式化后的字符串，如果发生错误返回空字符串
    std::string toString(const std::string &format = "") const;

    /// 日期是否有效
    bool valid();

    /// 年，[1970, )
    int year() const;

    /// 月，[1,12]
    int month() const;

    /// 日，[1,31]
    int day() const;

    /// 时，[0,23]
    int hour() const;

    /// 分，[0,59]
    int minute() const;

    /// 秒，[0,60]
    int second() const;

    /// 毫秒，[0,999]
    int millisecond() const;

    /// 获取一年中的第几天，[1,366]
    int yearDay() const;

    /// 获取一周中的第几天，[1,7]
    int weekDay() const;

    /// 时区，比如UTC+8的时区为8
    int timeZone() const;

    /// 时区偏移，单位秒，比如UTC+8的时区为28800
    int timeZoneOffset() const;

    /// 转换为UTC时间戳，单位毫秒 @note 比如1970-01-01 00:00:00为0
    int64_t timestamp() const;

    /// 设置时间缀来修改时间
    void setTimestamp(int64_t stamp, int stampTimeZone = DateTime::localTimeZone());

    /// 统一设置年月日来修改时间
    DateTime &setDateTime(int year, int month, int day, int hour, int minute, int second,
                          int millisecond, int stampTimeZone = DateTime::localTimeZone());

    /// 设置年，[1970, )
    /// \note 建议使用setDate统一设置年月日
    /// \see setDate
    DateTime &setYear(int year);

    /// 设置月，[1,12]
    /// \note 建议使用setDate统一设置年月日
    /// \see setDate
    DateTime &setMonth(int month);

    /// 设置日，[1,31]
    /// \note 建议使用setDate统一设置年月日
    /// \see setDate*/
    DateTime &setDay(int day);

    /// 设置时，[0,23]
    DateTime &setHour(int hour);

    /// 设置分，[0,59]
    DateTime &setMinute(int minute);

    /// 设置秒，[0,59]
    DateTime &setSecond(int second);

    /// 设置毫秒，[0,999]
    DateTime &setMilliSecond(int second);

    /// 设置为某个时间的开始
    /// \param period 时间类型，
    /// \details
    ///       为Year时置零为一年的开始，
    ///       为Month时置零为一月的开始，
    ///       为Day时置零为一天的开始，
    ///       为Hour时置零为一小时的开始，
    ///       为Minute时置零为一分钟的开始，
    ///       为Second/MilliSecond/MicroSecond时无效果
    DateTime &zeroSet(TimeSpan::Period period);

    /// 加/减 一段时间
    DateTime &add(long long value, TimeSpan::Period period);

    /// 加/减 一段时间
    DateTime &add(const TimeSpan &duration);

    /// 加/减 年
    DateTime &addYear(int value);

    /// 加/减 月
    DateTime &addMonth(int value);

    /// 加/减 周
    DateTime &addWeek(int value);

    /// 加/减 天
    DateTime &addDay(int value);

    /// 加/减 时
    DateTime &addHour(int value);

    /// 加/减 分
    DateTime &addMinute(int value);

    /// 加/减 秒
    DateTime &addSecond(int value);

    /// 加/减 毫秒
    DateTime &addMilliSecond(int value);

    /// 比较两时间之间的差异，self - other
    /// \param period 结果时间类型
    /// \param other 要比较的对象
    /// @details
    ///     为Year表示两者相差年份，不是绝对差值，2015-01-01与2014-12-30相差1年
    ///     为Month表示两者相差说数，不是绝对差值，2015-01-01与2014-12-30相差1月
    ///     为Day表示两者相差天数，不是绝对差值，2015-01-01 23:59:59与2015-01-02 00:00:00相差1天
    ///     为Hour表示两者相差小时数，不是绝对差值，2015-01-01 23:59:59与2015-01-02
    ///     00:00:00相差1小时 为Minute表示两者相差分钟数，不是绝对差值，2015-01-01
    ///     23:59:59与2015-01-02 00:00:00相差1分钟 为Second表示两者相差秒数
    ///     为MilliSecond表示两者相差毫秒数，Date的精度为秒，所以只是将相差秒数*1000
    ///     为MicroSecond表示两者相差微秒数，Date的精度为秒，所以只是将相差秒数*1000000
    /// \return 返回this - other的相应差值
    long long diff(const DateTime &other, TimeSpan::Period period = TimeSpan::MilliSecond);

    /// 距离1970-01-01 00:00:00的月数
    int getUTCFullMonths() const;

    /// 距离1970-01-01 00:00:00的年数
    int getUTCFullYears() const;

    /// 距离1970-01-01 00:00:00的毫秒数
    long long getUTCFullMilliSeconds() const;

    /// 距离1970-01-01 00:00:00的秒数
    /// \note 与getUTCStamp()含义相同
    /// \see getUTCStamp
    int64_t getUTCFullSeconds() const;

    /// 距离1970-01-01 00:00:00的分钟
    int getUTCFullMinutes() const;

    /// 距离1970-01-01 00:00:00的小时
    int getUTCFullHours() const;

    /// 距离1970-01-01 00:00:00的天数
    int getUTCFullDays() const;

    /// 距离1970-01-01 00:00:00的周数 @note 从0开始，1970-01-01 00:00:00为第0周星期4
    int getUTCFullWeeks() const;

    /// 是否是闰年
    bool isLeapYear() const;

    /// 是否是一月的最后一天
    bool isLastDayOfMonth() const;

    DateTime operator+(const TimeSpan &duration);
    DateTime operator-(const TimeSpan &duration);
    TimeSpan operator-(const DateTime &other);
    DateTime &operator+=(const TimeSpan &duration);
    DateTime &operator-=(const TimeSpan &duration);
    bool operator<(const DateTime &other);
    bool operator>(const DateTime &other);
    bool operator==(const DateTime &other);

protected:
    void update();

private:
    /*
   struct tm {
        int tm_sec;     // seconds after the minute - [0,59]
        int tm_min;     // minutes after the hour - [0,59]
        int tm_hour;    // hours since midnight - [0,23]
        int tm_mday;    // day of the month - [1,31]
        int tm_mon;     // months since January - [0,11]
        int tm_year;    // years since 1900
        int tm_wday;    // days since Sunday - [0,6],[周日到周六]
        int tm_yday;    // days since January 1 - [0,365]
        int tm_isdst;   // daylight savings time flag
   };*/
    struct tm m_tm;
    int m_millisecond; ///< 毫秒值
    bool m_valid;      ///< 日期是否有效，有可能初始化时提供的值不对

    int tm_gmtoff; /// 时区
};

} // namespace cppc
